#include "const/oPreDefine.h"
#include "platform/oUnitCache.h"
#include "platform/oUnitDef.h"
#include "platform/oTypes.h"
using namespace platform;
#include "physics/oBodyDef.h"
#define oCase case oUnitXml

oUnitCache::oUnitCache():
_currentDef(nullptr)
{
	_parser.setDelegator(this);
}

oUnitCache::~oUnitCache()
{
	oUnitCache::unload();
}

void oUnitCache::load( const char* filename )
{
	oUnitCache::unload();
	_parser.parse(filename);
}

void oUnitCache::unload()
{
	BOOST_FOREACH(const oUnitDefMap::value_type& item, _unitDefs)
	{
		item.second->release();
	}
	_unitDefs.clear();
}

oUnitDef* oUnitCache::getUnitDef( const string& name )
{
	auto it = _unitDefs.find(name);
	if (it != _unitDefs.end())
	{
		return it->second;
	}
	return nullptr;
}

oUnitCache& oUnitCache::sharedCache()
{
	static oUnitCache unitCache;
	return unitCache;
}

void oUnitCache::textHandler( void *ctx, const char *s, int len )
{ }

void oUnitCache::startElement( void *ctx, const char *name, const char **atts )
{
	switch (name[0])
	{
		oCase::Unit:
			{
				const char* name = nullptr;
				const char* desc = nullptr;
				CCSize size;
				float sensity;
				float density = 1.0f;
				float friction = 0.4f;
				float restitution = 0.4f;
				bool isStatic = false;
				const char* modelFile = nullptr;
				float move = 0;
				float detectDistance = 0;
				float maxHp = 0;
				float attack = 0;
				float attackDelay = 0;
				float attackEffectDelay = 0;
				float attackDistance = 0;
				uint32 attackType = 0;
				uint32 attackTarget = 0;
				uint32 damageType = 0;
				uint32 defenceType = 0;
				int bulletType = -1;
				int attackEffect = -1;
				int hitEffect = -1;
				const char* sndAttack = nullptr;
				const char* sndDeath = nullptr;
				const char* actionStr = nullptr;
				for (int i = 0;atts[i] != nullptr;i++)
				{
					switch (atts[i][0])
					{
						oCase::Name:
							name = atts[++i];
							break;
						oCase::Desc:
							desc = atts[++i];
							break;
						oCase::Size:
							oGetPosFromStr(atts[++i], size.width, size.height);
							break;
						oCase::Sensity:
							sensity = (float)atof(atts[++i]);
							break;
						oCase::Density:
							density = (float)atof(atts[++i]);
							break;
						oCase::Friction:
							friction = (float)atof(atts[++i]);
							break;
						oCase::Restitution:
							restitution = (float)atof(atts[++i]);
							break;
						oCase::IsStatic:
							isStatic = (atoi(atts[++i]) != 0);
							break;
						oCase::Model:
							modelFile = atts[++i];
							break;
						oCase::Move:
							move = (float)atof(atts[++i]);
							break;
						oCase::DetectDistance:
							detectDistance = (float)atof(atts[++i]);
							break;
						oCase::MaxHp:
							maxHp = (float)atof(atts[++i]);
							break;
						oCase::Attack:
							attack = (float)atof(atts[++i]);
							break;
						oCase::AttackDelay:
							attackDelay = (float)atof(atts[++i]);
							break;
						oCase::AttackEffectDelay:
							attackEffectDelay = (float)atof(atts[++i]);
							break;
/*
						oCase::AttackDistance:
							attackDistance = (float)atof(atts[++i]);
							break;
*/
						oCase::AttackType:
							attackType = (uint32)atoi(atts[++i]);
							break;
						oCase::AttackTarget:
							attackTarget = (uint32)atoi(atts[++i]);
							break;
						oCase::DamageType:
							damageType = (uint32)atoi(atts[++i]);
							break;
						oCase::DefenceType:
							defenceType = (uint32)atoi(atts[++i]);
							break;
						oCase::BulletType:
							bulletType = atoi(atts[++i]);
							break;
						oCase::AttackEffect:
							attackEffect = atoi(atts[++i]);
							break;
						oCase::HitEffect:
							hitEffect = atoi(atts[++i]);
							break;
						oCase::SndAttack:
							sndAttack = atts[++i];
							break;
						oCase::SndDeath:
							sndDeath = atts[++i];
							break;
						oCase::Actions:
							actionStr = atts[++i];
							break;
					}
				}
				_currentDef = oUnitDef::create(modelFile, size, density, friction, restitution);
				_currentDef->name = name;
				_currentDef->type = oFNVHash(name);
				_currentDef->desc = desc;
				_currentDef->sensity = sensity;
				_currentDef->setIsStatic(isStatic);
				_currentDef->move = move;
				_currentDef->detectDistance = detectDistance;
				_currentDef->maxHp = maxHp;
				_currentDef->attackBase = attack;
				_currentDef->attackDelay = attackDelay;
				_currentDef->attackEffectDelay = attackEffectDelay;
				//_currentDef->attackDistance = attackDistance;
				_currentDef->attackType = (oAttackType)attackType;
				_currentDef->attackTarget = (oAttackTarget)attackTarget;
				_currentDef->damageType = (oDamageType)damageType;
				_currentDef->defenceType = (oDefenceType)defenceType;
				_currentDef->bulletType = bulletType;
				_currentDef->attackEffect = attackEffect;
				_currentDef->hitEffect = hitEffect;
				_currentDef->sndAttack = sndAttack;
				_currentDef->sndDeath = sndDeath;
			}
			break;
		oCase::Vertex:
			for (int i = 0;atts[i] != nullptr;i++)
			{
				switch (atts[i][0])
				{
					oCase::Pos:
						{
							b2Vec2 vec;
							oGetPosFromStr(atts[++i], vec.x, vec.y);
							_vertices.push_back(vec);
						}
						break;
				}
			}
			break;
		oCase::TargetAllow:
			for (int i = 0;atts[i] != nullptr;i++)
			{
				char n = atts[i][0];
				if (atoi(atts[++i]) != 0)
				{
					switch (n)
					{
						oCase::Friend:
							_currentDef->targetAllow.allow(e_friend);
							break;
						oCase::Enemy:
							_currentDef->targetAllow.allow(e_enemy);
							break;
						oCase::Neutral:
							_currentDef->targetAllow.allow(e_neutral);
							break;
						oCase::Terrain:
							_currentDef->targetAllow.allowTerrain(true);
							break;
					}
				}
			}
			break;
		oCase::Rectangle:
			{
				b2Vec2 center;
				float width = 0;
				float height = 0;
				float angle = 0;
				float density = 1.0f;
				float friction = 0.4f;
				float restitution = 0.4f;
				for (int i = 0;atts[i] != nullptr;i++)
				{
					switch (atts[i][0])
					{
						oCase::Center:
							oGetPosFromStr(atts[++i], center.x, center.y);
							break;
						oCase::Width:
							width = (float)atof(atts[++i]);
							break;
						oCase::Height:
							height = (float)atof(atts[++i]);
							break;
						oCase::Angle:
							angle = (float)atof(atts[++i]);
							break;
						oCase::Density:
							density = (float)atof(atts[++i]);
							break;
						oCase::Friction:
							friction = (float)atof(atts[++i]);
							break;
						oCase::Restitution:
							restitution = (float)atof(atts[++i]);
							break;
					}
				}
				_currentDef->getBodyDef()->attachPolygon(
					center, width, height, angle,
					density, friction, restitution);
			}
			break;
		oCase::Polygon:
			{
				for (int i = 0;atts[i] != nullptr;i++)
				{
					switch (atts[i][0])
					{
						oCase::Density:
							_density = (float)atof(atts[++i]);
							break;
						oCase::Friction:
							_friction = (float)atof(atts[++i]);
							break;
						oCase::Restitution:
							_restitution = (float)atof(atts[++i]);
							break;
					}
				}
			}
			break;
		oCase::Loop:
			{
				for (int i = 0;atts[i] != nullptr;i++)
				{
					switch (atts[i][0])
					{
						oCase::Friction:
							_friction = (float)atof(atts[++i]);
							break;
						oCase::Restitution:
							_restitution = (float)atof(atts[++i]);
							break;
					}
				}
			}
			break;
		oCase::Circle:
			{
				b2Vec2 center;
				float radius = 0;
				float density = 1.0f;
				float friction = 0.4f;
				float restitution = 0.4f;
				for (int i = 0;atts[i] != nullptr;i++)
				{
					switch (atts[i][0])
					{
						oCase::Center:
							oGetPosFromStr(atts[++i], center.x, center.y);
							break;
						oCase::Radius:
							radius = (float)atof(atts[++i]);
							break;
						oCase::Density:
							density = (float)atof(atts[++i]);
							break;
						oCase::Friction:
							friction = (float)atof(atts[++i]);
							break;
						oCase::Restitution:
							restitution = (float)atof(atts[++i]);
							break;
					}
				}
				_currentDef->getBodyDef()->attachCircle(
					center, radius,
					density, friction, restitution);
			}
			break;
	}
}

void oUnitCache::endElement( void *ctx, const char *name )
{
	switch (name[0])
	{
		oCase::Polygon:
			_currentDef->getBodyDef()->attachPolygon(
				_vertices, _density, _friction, _restitution);
			break;
		oCase::Loop:
			_currentDef->getBodyDef()->attachLoop(
				_vertices, _friction, _restitution);
			break;
		oCase::Unit:
			_currentDef->retain();
			_unitDefs[_currentDef->name] = _currentDef;
			break;
	}
}
